#pragma once

#include "Common.h"
#include "PageCache.h"


class CentralCache
{
public:
	static CentralCache* GetInstance()
	{
		return _sInst;
	}


	// 获取一个非空span
	Span* GetOneSpan(SpanList& _freelist, size_t size)
	{
		Span* it = _freelist.Begin();
		while (it != _freelist.End())
		{
			if (it->_freelist != nullptr)
			{
				return it;
			}
			else
			{
				it = it->_next;
			}
		}
		// 先把central cache的桶锁解了，这样不会阻塞其他线程释放内存对象
		_freelist._mtx.unlock();

		// 到了这说明没有空闲的span，需要向PageCache获取
		PageCache::GetInstance()->_pageMtx.lock();
		Span* span = PageCache::GetInstance()->NewSpan(SizeClass::NumMovePage(size));
		span->_isUse = true;
		span->_ObjSize = size; // 记录该span切分成小块内存的size
		PageCache::GetInstance()->_pageMtx.unlock();

		// 对span切分成小块,不需要加锁
		// 计算span的大块内存的起始地址和内存大小（字节）
		char* start = reinterpret_cast<char*>(span->_pageid << PAGE_SHIFT);
		size_t bytes = span->_n << PAGE_SHIFT;
		char* end = start + bytes;

		// 把大块内存切成自由链表连接起来
		// 先切一块做头，方便之后尾插
		span->_freelist = start;
		start += size;
		void* tail = span->_freelist;

		while (start < end)
		{
			NextObj(tail) = start;
			tail = start;
			start += size;
		}
		// 切完要把尾部置空，否则出现严重bug
		NextObj(tail) = nullptr;
		// 切好后需要把span挂到相应桶，这里把锁加回去
		_freelist._mtx.lock();
		_freelist.PushFront(span);
		return span;
	}

	// 从中心缓存获取一定数量的对象给threadcache
	size_t FetchRangeObj(void*& start, void*& end, size_t batchNum, size_t size)
	{
		size_t index = SizeClass::Index(size);
		_spanList[index]._mtx.lock();
		// 从span中获取对象
		// 如果不够batchNum，有多少拿多少
		Span* span = GetOneSpan(_spanList[index],size);
		assert(span);
		assert(span->_freelist);
		start = span->_freelist;
		end = span->_freelist;
		size_t i = 0;
		size_t actualNum = 1;
		while (i < batchNum - 1 && NextObj(end))
		{
			end = NextObj(end);
			++i;
			++actualNum;
		}

		span->_freelist = NextObj(end);
		NextObj(end) = nullptr;
		// 记录拿走了span多少
		span->_useCount += actualNum;
		_spanList[index]._mtx.unlock();

		return actualNum; // 返回实际拿到的对象数量
	}

	// 从ThreadCache释放部分对象回CentralCache
	void ReleaseListToSpans(void* start,size_t size)
	{
		// cout << "CentrtalCache:从ThreadCache释放部分对象回CentralCache:" << std::this_thread::get_id() << endl;
		// 先计算属于哪个桶
		size_t index = SizeClass::Index(size);
		_spanList[index]._mtx.lock();
		while (start)
		{
			void* next = NextObj(start);
			Span* span = PageCache::GetInstance()->MapObjToSpan(start);
			// 头插到_freelist
			NextObj(start) = span->_freelist;
			span->_freelist = start;
			--span->_useCount; // 使用计数--

			// 当span切分出去的小块内存都回来了
			if (span->_useCount == 0)
			{
				_spanList[index].Erase(span);
				span->_freelist = nullptr;
				span->_next = nullptr;
				span->_prev = nullptr;
				// 释放桶锁，防止阻塞其他线程申请桶锁
				_spanList[index]._mtx.unlock();
				// 申请PageCache的锁,准备把Span还给PageCache
				PageCache::GetInstance()->_pageMtx.lock();
				PageCache::GetInstance()->ReleaseSpanToPageCache(span);
				PageCache::GetInstance()->_pageMtx.unlock();
				// 拿回桶锁
				_spanList[index]._mtx.lock();

			}
			start = next;
		}

		_spanList[index]._mtx.unlock();

	}

private:
	CentralCache()
	{}

	CentralCache(const CentralCache&) = delete;

	SpanList _spanList[NUMFREESIZE];

	static CentralCache* _sInst;
};